// ====================  多生产多消费模型 ==========================

#pragma once

#include <pthread.h>
#include <queue>
#include <iostream>

template <class T>
class BlockQueue
{
    // 静态成员变量属于整个类而不是类对象
    static int DEFAULT;

public:
    // 构造
    BlockQueue(int maxcap = DEFAULT)
        : _cap(DEFAULT)
    {
        // 初始化锁
        pthread_mutex_init(&_mtx, nullptr);
        // 初始化条件变量
        pthread_cond_init(&c_cond, nullptr);
        pthread_cond_init(&p_cond, nullptr);
    }

    // 析构
    ~BlockQueue()
    {
        // 销毁锁
        pthread_mutex_destroy(&_mtx);
        // 销毁条件变量
        pthread_cond_destroy(&c_cond);
        pthread_cond_destroy(&p_cond);
    }

    // 生产数据
    void push(const T &in)
    {
        pthread_mutex_lock(&_mtx);
        // 如果阻塞队列满了，就不能生产数据了
        while (_q.size() == _cap) // 防止伪唤醒
        {
            // 那么线程需要阻塞，不能生产了
            pthread_cond_wait(&p_cond, &_mtx); // 自动释放锁，线程处于阻塞状态
        }
        // 来到这有两种情况：
        // 1. 队列没满
        // 2. 生产者线程被唤醒 (被唤醒表示满足了某个条件，即阻塞队列没满)
        _q.push(in);
        // 执行上一条语句后，阻塞队列一定有数据，说明可以唤醒消费者线程
        pthread_cond_signal(&c_cond); // 该语句放到临界区里外都可以
        pthread_mutex_unlock(&_mtx);
    }

    // 取数据
    T pop()
    {
        pthread_mutex_lock(&_mtx);
        // 阻塞队列没有数据不能消费，即不能取数据
        while (_q.size() == 0) // 防止伪唤醒
        {
            // 注意：生产者和消费者不能用同一个条件变量
            // 否则唤醒的是生产者线程还是消费者线程不确定
            pthread_cond_wait(&c_cond, &_mtx);
        }
        // 来到这有两种情况：
        // 1. 阻塞队列有数据
        // 2. 消费者线程被唤醒 (被唤醒表示满足了某个条件，即阻塞队列有数据)
        T out = _q.front();
        _q.pop();
        // 消费者执行上一条语句，说明阻塞队列一定还没满，说明可以唤醒生产者线程
        pthread_cond_signal(&p_cond); // 该语句放到临界区里外都可以
        pthread_mutex_unlock(&_mtx);

        return out;
    }

private:
    std::queue<T> _q;      // 队列，共享资源
    size_t _cap;           // 阻塞队列的容量
    pthread_mutex_t _mtx;  // 互斥锁。保护共享资源
    pthread_cond_t c_cond; // 消费者线程条件变量，实现同步
    pthread_cond_t p_cond; // 生产者线程条件变量，实现同步
};

template <class T>
int BlockQueue<T>::DEFAULT = 5; // 初始化静态成员变量

// ============================= 单生产单消费模型 ===========================
// #pragma once

// #include <pthread.h>
// #include <queue>
// #include <iostream>

// template <class T>
// class BlockQueue
// {
//     // 静态成员变量属于整个类而不是类对象
//     static int DEFAULT;

// public:
//     // 构造
//     BlockQueue(int maxcap = DEFAULT)
//         : _cap(DEFAULT)
//     {
//         // 初始化锁
//         pthread_mutex_init(&_mtx, nullptr);
//         // 初始化条件变量
//         pthread_cond_init(&c_cond, nullptr);
//         pthread_cond_init(&p_cond, nullptr);
//     }

//     // 析构
//     ~BlockQueue()
//     {
//         // 销毁锁
//         pthread_mutex_destroy(&_mtx);
//         // 销毁条件变量
//         pthread_cond_destroy(&c_cond);
//         pthread_cond_destroy(&p_cond);
//     }

//     // 生产数据
//     void push(const T &in)
//     {
//         pthread_mutex_lock(&_mtx);
//         // 如果阻塞队列满了，就不能生产数据了
//         if (_q.size() == _cap)
//         {
//             // 那么线程需要阻塞，不能生产了
//             pthread_cond_wait(&p_cond, &_mtx); // 自动释放锁，线程处于阻塞状态
//         }
//         // 来到这有两种情况：
//         // 1. 队列没满
//         // 2. 生产者线程被唤醒 (被唤醒表示满足了某个条件，即阻塞队列没满)
//         _q.push(in);
//         // 执行上一条语句后，阻塞队列一定有数据，说明可以唤醒消费者线程
//         pthread_cond_signal(&c_cond); // 该语句放到临界区里外都可以
//         pthread_mutex_unlock(&_mtx);
//     }

//     // 取数据
//     T pop()
//     {
//         pthread_mutex_lock(&_mtx);
//         // 阻塞队列没有数据不能消费，即不能取数据
//         if (_q.size() == 0)
//         {
//             // 注意：生产者和消费者不能用同一个条件变量
//             // 否则唤醒的是生产者线程还是消费者线程不确定
//             pthread_cond_wait(&c_cond, &_mtx);
//         }
//         // 来到这有两种情况：
//         // 1. 阻塞队列有数据
//         // 2. 消费者线程被唤醒 (被唤醒表示满足了某个条件，即阻塞队列有数据)
//         T out = _q.front();
//         _q.pop();
//         // 消费者执行上一条语句，说明阻塞队列一定还没满，说明可以唤醒生产者线程
//         pthread_cond_signal(&p_cond); // 该语句放到临界区里外都可以
//         pthread_mutex_unlock(&_mtx);

//         return out;
//     }

// private:
//     std::queue<T> _q;      // 队列，共享资源
//     size_t _cap;           // 阻塞队列的容量
//     pthread_mutex_t _mtx;  // 互斥锁。保护共享资源
//     pthread_cond_t c_cond; // 消费者线程条件变量，实现同步
//     pthread_cond_t p_cond; // 生产者线程条件变量，实现同步
// };

// template <class T>
// int BlockQueue<T>::DEFAULT = 5; // 初始化静态成员变量

// BlockQueue<int>::myStaticVar = 10; // 通过类名访问静态成员变量并赋值

// #pragma once

// #include <pthread.h>
// #include <queue>
// #include <iostream>

// template <class T>
// class BlockQueue
// {
//     // 静态成员变量属于整个类而不是类对象
//     static int DEFAULT;

// public:
//     // 构造
//     BlockQueue(int maxcap = DEFAULT)
//         : _cap(DEFAULT)
//     {
//         // 初始化锁
//         pthread_mutex_init(&_mtx, nullptr);
//         // 初始化条件变量
//         pthread_cond_init(&c_cond, nullptr);
//         pthread_cond_init(&p_cond, nullptr);

//         low_water = maxcap / 3;
//         high_water = maxcap * 2 / 3;
//     }

//     // 析构
//     ~BlockQueue()
//     {
//         // 销毁锁
//         pthread_mutex_destroy(&_mtx);
//         // 销毁条件变量
//         pthread_cond_destroy(&c_cond);
//         pthread_cond_destroy(&p_cond);
//     }

//     // 生产数据
//     void push(const T &in)
//     {
//         pthread_mutex_lock(&_mtx);
//         // 如果阻塞队列满了，就不能生产数据了
//         if (_q.size() == _cap)
//         {
//             // 那么线程需要阻塞，不能生产了
//             pthread_cond_wait(&p_cond, &_mtx); // 自动释放锁，线程处于阻塞状态
//         }
//         // 来到这有两种情况：
//         // 1. 队列没满
//         // 2. 生产者线程被唤醒 (被唤醒表示满足了某个条件，即阻塞队列没满)
//         _q.push(in);
//         if (_q.size() > high_water)
//         {
//             // 执行上一条语句后，阻塞队列一定有数据，说明可以唤醒消费者线程
//             pthread_cond_signal(&c_cond); // 该语句放到临界区里外都可以
//         }
//         pthread_mutex_unlock(&_mtx);
//     }

//     // 取数据
//     T pop()
//     {
//         pthread_mutex_lock(&_mtx);
//         // 阻塞队列没有数据不能消费，即不能取数据
//         if (_q.size() == 0)
//         {
//             // 注意：生产者和消费者不能用同一个条件变量
//             // 否则唤醒的是生产者线程还是消费者线程不确定
//             pthread_cond_wait(&c_cond, &_mtx);
//         }
//         // 来到这有两种情况：
//         // 1. 阻塞队列有数据
//         // 2. 消费者线程被唤醒 (被唤醒表示满足了某个条件，即阻塞队列有数据)
//         T out = _q.front();
//         _q.pop();
//         if (_q.size() < low_water)
//         {
//             // 消费者执行上一条语句，说明阻塞队列一定还没满，说明可以唤醒生产者线程
//             pthread_cond_signal(&p_cond); // 该语句放到临界区里外都可以
//         }
//         pthread_mutex_unlock(&_mtx);

//         return out;
//     }

// private:
//     std::queue<T> _q;      // 队列，共享资源
//     size_t _cap;           // 阻塞队列的容量
//     pthread_mutex_t _mtx;  // 互斥锁。保护共享资源
//     pthread_cond_t c_cond; // 消费者线程条件变量，实现同步
//     pthread_cond_t p_cond; // 生产者线程条件变量，实现同步

//     int low_water;
//     int high_water;
// };

// template <>
// int BlockQueue<int>::DEFAULT = 20; // 初始化静态成员变量

// // BlockQueue<int>::myStaticVar = 10; // 通过类名访问静态成员变量并赋值